package com.ktjy.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.ktjy.config.SecurityConfig;
import com.ktjy.dto.LoginUser;
import com.ktjy.dto.WorkFlowDTO;
import com.ktjy.mapper.TbActivityMapper;
import com.ktjy.pojo.TbActivity;
import com.ktjy.service.WorkFlowService;
import com.ktjy.utils.Constants;
import com.ktjy.utils.PageUtil;
import com.ktjy.vo.ActCommentEntity;
import com.ktjy.vo.ActTaskEntity;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class WorkFlowServiceImpl implements WorkFlowService {
    @Autowired
    TbActivityMapper activityMapper;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;

    /**
     * 启动流程和完成任务
     *
     * @param processDefinitionKey
     * @param id
     * @return
     */
    @Override
    public void startProcess(String processDefinitionKey, Integer id) {
        //业务key
        String businessKey = processDefinitionKey + ":" + id;
        //任务处理人为当前登录用户
        Map<String, Object> variables = new HashMap<>();
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        variables.put("username", loginUser.getRealName());
        //1.启动流程实例
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, variables);
        //2.完成任务
        Task task = taskService.createTaskQuery().processInstanceId(processInstance.getId()).singleResult(); //根据流程实例id查询当前任务
        taskService.complete(task.getId());
        //3.修改状态
        UpdateWrapper uw = new UpdateWrapper();
        uw.eq("id", id); //活动id
        uw.set("status", Constants.STATUS_AUDITING);
        activityMapper.update(uw);
    }

    // 查询当前登录用户的待办任务
    @Override
    public PageUtil findTask(WorkFlowDTO workFlowDTO,
                             Integer pageNo, Integer pageSize) {
        PageUtil pageUtil = new PageUtil();
        pageUtil.setPageNo(pageNo);
        pageUtil.setPageSize(pageSize);
        //1、得到任务办理人信息
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        // 2、创建任务查询对象
//        TaskQuery taskQuery = taskService.createTaskQuery()
//                .taskAssignee(loginUser.getRealName());
        // Activiti 引擎在查询候选用户任务时使用的是 Spring Security 的 UserDetailsServiceImpl 返回 UserDetail实现类对象(LoginUser)来验证用户是否存在。 //同时会调用 LoginUser 类中的 getAuthorities() 方法,获取权限信息
        System.out.println("loginUser.getRealName(): " + loginUser.getRealName());


        TaskQuery taskQuery = taskService.createTaskQuery()
                .or()
                .taskAssignee(loginUser.getRealName()) // 任务办理人
                .taskCandidateUser(loginUser.getRealName()) // 任务候选人
                .endOr();

        //针对发起人查询任务
        if (taskQuery.count() == 0) {
            taskQuery = taskService.createTaskQuery()
                    .taskAssignee(loginUser.getRealName());
        }
        // 只有当 workFlowDTO 不为 null 时才添加时间筛选条件
        try {
            if (workFlowDTO != null) {
                if (workFlowDTO.getBeginTime() != null) {
                    Date beginDate = new SimpleDateFormat("yyyy-MM-dd").parse(workFlowDTO.getBeginTime());
                    taskQuery.taskCreatedAfter(beginDate);
                }
                if (workFlowDTO.getEndTime() != null) {
                    Date endDate = new SimpleDateFormat("yyyy-MM-dd").parse(workFlowDTO.getEndTime());
                    taskQuery.taskCreatedBefore(endDate);
                }
            }
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        //2、查询当前登录用户的所有待办理任务的总数
        long count = taskQuery.count();
        pageUtil.setTotalCount(count);
        if (count % pageSize == 0) {
            pageUtil.setTotalPageCount(count / pageSize);
        } else {
            pageUtil.setTotalPageCount(count / pageSize + 1);
        }
        //3、查询当前登录用户的所有待办理任务列表
        List<Task> tasks = taskQuery.listPage((pageNo - 1) * pageSize, pageSize);
        List<ActTaskEntity> list = new ArrayList<>();
        if (!ObjectUtils.isEmpty(tasks)) {
            for (Task task : tasks) {
                ActTaskEntity actTaskEntity = new ActTaskEntity();
                //对象属性复制
                BeanUtils.copyProperties(task, actTaskEntity);
                // 设置任务状态显示
                if (task.getAssignee() != null && !task.getAssignee().isEmpty()) {
                    actTaskEntity.setAssignee(loginUser.getRealName());
                } else {
                    actTaskEntity.setAssignee("待认领");
                }
                list.add(actTaskEntity);
            }
            pageUtil.setData(list);
        }
        return pageUtil;
    }

    @Override
    public TbActivity findActivityByTaskId(String taskId) {
        //1、根据任务ID查询任务实例
        Task task = taskService.createTaskQuery()
                .taskId(taskId).singleResult();
        //2、从任务实例对象中取出流程实例ID
        String processInstanceId = task.getProcessInstanceId();
        //3、根据流程实例ID查询流程实例
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        //4、从流程实例对象中取出business_key，business_key数据格式为： LeaveBill:1 这个1为请假单ID
        String businessKey = processInstance.getBusinessKey();
        String businessId = businessKey.split(":")[1]; // 得到business_key中的请假单ID
        //5、最后根据业务ID查询业务对象信息
        return activityMapper.selectById(businessId);
    }

    // 根据任务ID查询批注信息
    @Override
    public List<ActCommentEntity> findCommentByTaskId(String taskId) {
        //1、根据任务ID查询任务实例
        Task task = taskService.createTaskQuery()
                .taskId(taskId).singleResult();
        //2、从任务里面取出流程实例ID
        String processInstanceId = task.getProcessInstanceId();
        //3、通过流程实例ID得到其所有的批注列表信息
        List<Comment> comments = taskService.getProcessInstanceComments(processInstanceId);
        //因为上述comments在转换成JSON格式时会出错，所以这里自定义一个名为ActCommentEntity实体类
        List<ActCommentEntity> list = new ArrayList<>();
        if (!ObjectUtils.isEmpty(comments)) {
            for (Comment comment : comments) {
                ActCommentEntity entity = new ActCommentEntity();
                BeanUtils.copyProperties(comment, entity);
                list.add(entity);
            }
        }
        return list;
    }

    @Override
    public void completeTask(WorkFlowDTO workFlowDTO) {
        //1、获取从前端传递过来的任务ID
        String taskId = workFlowDTO.getTaskId();
        //2、获取从前端传递过来的连接名称
        String outcomt = workFlowDTO.getOutcomt();
        // 因为任务处理完后还需要修改当前请假单的审批状态
        //3、获取从前端传递过来的请假单ID
        Integer businessId = workFlowDTO.getBusinessId();
        //4、获取从前端传递过来的批注信息
        String comment = workFlowDTO.getComment() == null ? "无" : workFlowDTO.getComment();

        //5、根据任务ID查询任务实例
        Task task = taskService.createTaskQuery()
                .taskId(taskId).singleResult();
        //6、从任务里面取出流程实例ID
        String processInstanceId = task.getProcessInstanceId();
        //7、设置批注人名
        LoginUser loginUser = (LoginUser) SecurityContextHolder.getContext()
                .getAuthentication().getPrincipal();
        //Authentication需导包： import org.activiti.engine.impl.identity.Authentication;
        Authentication.setAuthenticatedUserId(loginUser.getRealName());
//        taskService.claim(taskId, loginUser.getRealName()); //认领任务设置认领人 认领后该任务只能由该用户处理
        //8、添加批注信息 (act_hi_comment批注表)
        taskService.addComment(taskId, processInstanceId, "[" + outcomt + "]" + comment);
        //9、完成任务
        Map<String, Object> variables = new HashMap<>();
        variables.put("outcomt", outcomt);
        // 在执行完下面taskService.complete()代码完成任务(提交而非放弃的情况下)，会第一时间触发TaskListenerImpl任务监听器类的notify()方法(可以在监听器类中打上断点进行调试)。
        taskService.complete(taskId, variables);
        //10、判断任务是否结束
        // 上面调用taskService.complete()执行完任务后，只要是任务完毕，这条任务数据在act_ru_execution表中会被删除，使用processInstanceId任务ID去查询，得到的processInstance结果会是null
        ProcessInstance processInstance = runtimeService
                .createProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        // 11、修改请假单状态
        UpdateWrapper uw = new UpdateWrapper();
        uw.eq("id", businessId);
        if (outcomt.equals("驳回")) {
            uw.set("status", Constants.STATUS_REJECT);//3 表示已驳回
            activityMapper.update(uw);
        }
        if (processInstance == null) {// 等于null说明流程结束了 说明是最后一个岗位的人审批了,最后审批通过,才可以将活动状态改为通过
            if (outcomt.equals("同意")) {
                uw.set("status", Constants.STATUS_PASS);//4 表示已通过
                activityMapper.update(uw);
            }
        }
    }
}
